from abc import abstractmethod
from socket import *
import threading
import time
import numpy as np
import cv2
from threading import Thread

import queue


PHYSICAL = 1
SIMULATE = 2
MONITOR = 3

Servers = []
def Shutdown():
    for s in Servers:
        s.shutdown()
SHUTDOWN_LOCK = threading.Lock()

class CameraTcpServer(object):
    _ip = '127.0.0.1'
    _port = 42000
    _clients = []

    _mute = True

    def __init__(self):
        Servers.append(self)

        self.code_mode = "utf-8"    #收发数据编码/解码格式
        self.server_socket = socket(AF_INET, SOCK_STREAM)   #创建socket
        self.server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, True)   #设置端口复用
        self.server_socket.bind((self._ip, self._port))     #绑定IP和Port
        self.server_socket.listen(10)  #设置为被动socket
        print('[+] Camera服务器端开启 addr = {}'.format((self._ip, self._port)))
        print("[+] Listen...")
    
    def run(self):
        while True:
            try:
                client_socket, client_addr = self.server_socket.accept()    #等待客户端连接
                print("[+][TCP] {} online".format(client_addr))
            except:
                break
            
            self._clients.append((client_socket,client_addr))
            thread_recv = Thread(target=self.recv_data, args=(client_socket, client_addr)) 
            thread_recv.daemon = True 
            thread_recv.start()  #开启线程
 
    def recv_size(self,conn, count):
        buf = b''#buf是一个byte类型
        while count:
            try:
                newbuf = conn.recv(count)
            except:
                return False,0
            if not newbuf: 
                return False,0
            buf += newbuf
            count -= len(newbuf)
        return True,buf

    window_name = "Unity"
    
    def recv_data(self, client_socket, client_addr):
        while True:   
            ret,img_size = self.recv_size(client_socket,16)
            if ret:
                client_socket.send('Get'.encode(self.code_mode))
                pic_sz =  int(str(img_size, encoding=self.code_mode))
                if not self._mute:
                    print("[TCP] {0}  \t from {1} 消息：{2}".format(
                        time.asctime(time.localtime()), client_addr,'img size = {}'.format(pic_sz)))
                if (pic_sz == 0):
                    Shutdown()
                    break
                ret, img_buf = self.recv_size(client_socket,pic_sz)
                if ret:
                    if not self._mute:
                        print("[TCP] {0}  \t from {1} 消息：{2}".format(
                        time.asctime(time.localtime()), client_addr,'one picture '))

                    data = np.frombuffer(img_buf, np.uint8)
                    img = cv2.imdecode(data, cv2.IMREAD_COLOR)
                    # self.pic_load(self.window_name,img)

                else:
                    print('[+] Client {} leave while sending image...'.format(client_addr))
                    client_socket.close()
                    break
            else:
                print('[+] Client {} leave...'.format(client_addr))
                client_socket.close()
                break
        cv2.destroyAllWindows()
    
    def pic_load(self,win_name,img):
        # cv2.imwrite('server_pic//{0}.jpg'.format(time.localtime()),img)#写出文件
        cv2.imshow(win_name, img)
        cv2.waitKey(1)

    def shutdown(self):
        cv2.destroyAllWindows()
        for (c,c_addr) in self._clients:
            print('[+] 主动关闭 Client {} 的连接...'.format(c_addr))
            c.close()
        print('[+] Camera服务器端关闭 addr = {}'.format((self._ip, self._port)))
        self.server_socket.close()

class UdpServer(object):
    _grd_client = None
    _phy_client = None
    _sim_client = None
    _admin_client = None
    _clients_lock = threading.Lock()

    _send_msg_queue = queue.Queue(64)
    _send_msg_lock = threading.Lock()

    def __init__(self,name,ip,port):
        self._ip = ip
        self._port = port
        self._name = name

        Servers.append(self)
        self.code_mode = "utf-8"    
        self.server_socket = socket(AF_INET, SOCK_DGRAM)   #创建socket
        self.server_socket.setsockopt(SOL_SOCKET, SO_REUSEADDR, True)   #设置端口复用
        self.server_socket.bind((self._ip, self._port))     #绑定IP和Port
        print('[+] {} 服务器端开启 addr = {}'.format(self._name,(self._ip, self._port)))
        self._send_msg_queue.queue.clear()

    def send_msg(self):
        while True:
            self._send_msg_lock.acquire()
            
            if (not self._send_msg_queue.empty()):
                msg,addr = self._send_msg_queue.get()
                print(msg,addr)
                self.server_socket.sendto(msg.encode(self.code_mode), addr) 

            self._send_msg_lock.release()
    
    
    def push_send_msg(self,msg,addr):
        if (addr == None): return 
        self._send_msg_lock.acquire()
        
        if (self._send_msg_queue.full()):
            self._send_msg_queue.get()
        self._send_msg_queue.put((msg,addr))

        self._send_msg_lock.release()
    
    def run(self):
        t=threading.Thread(target=self.send_msg,args=())
        t.daemon = True 
        t.start()

        while True:
            try:
                data, client_addr = self.server_socket.recvfrom(1024)
                data = data.decode(self.code_mode)
                self.print_log(client_addr,data)
            except:
                break
            self.set_client(client_addr,data)
            t=threading.Thread(target=self.handle_recv_msg,args=(data,client_addr,))
            t.daemon = True 
            t.start()

    @abstractmethod
    def print_log(self,client_addr,data):
        print("[{3}] {0}  \t from {1} 消息：{2}".format(
                time.asctime(time.localtime()), client_addr, data,self._name))
        pass

    @abstractmethod
    def handle_recv_msg(self,msg,addr):
        pass
    
    def set_client(self,addr,data):
        self._clients_lock.acquire()
        if data.startswith("Initial:"):
            if data.find("physical")!=-1:
                self._phy_client = addr
            elif data.find("guard")!=-1:
                self._grd_client = addr
            elif data.find("simulate")!=-1:
                self._grd_client = addr
            elif data.find("test")!=-1:
                self._admin_client = addr
        self._clients_lock.release()
        return 

    def shutdown(self):
        SHUTDOWN_LOCK.acquire()
        print('[+] {} 服务器端关闭 addr = {}'.format(self._name,(self._ip, self._port)))
        for client_addr in [self._phy_client,self._grd_client,self._admin_client]:
            if (client_addr):
                msg = 'quit'
                self.server_socket.sendto(msg.encode(self.code_mode), client_addr)
        self.server_socket.close()
        SHUTDOWN_LOCK.release()

class IrSensorUdpServer(UdpServer):
    def __init__(self):
        self._file_fp = None
        self._file_print = False
        name='IrSensor'
        ip = '127.0.0.1'
        port = 41997
        super().__init__(name,ip,port)

    def handle_recv_msg(self,msg,addr):
        if (msg.startswith("Initial:")):
            if (self._file_print):
                self._file_fp = open("log-{}.txt".format(time.time()),"w")
            return
        elif (msg == 'shutdown'):
            if self._file_print and self._file_fp:
                self._file_fp.close()
                self._file_fp = None
            Shutdown()
            return
        elif msg == 'quit':
            if self._file_print and self._file_fp:
                self._file_fp.close()
                self._file_fp = None
            self._clients_lock.acquire()
            if addr == self._phy_client: 
                self._phy_client = None
            if addr == self._grd_client: 
                self._grd_client = None
            if addr == self._admin_client:
                self._admin_client = None
            self._clients_lock.release()
            return

        if addr == self._phy_client:
            # msg of x,y,deg
            if (self._grd_client):
                self.push_send_msg(msg,self._grd_client)
        
        elif addr == self._grd_client:
            # fake unity test of F,R,B,L
            if (self._phy_client):
                self.push_send_msg(msg,self._phy_client)

    
    def print_log(self,client_addr,data):
        st = data
        if data.startswith("{"):
            try:
                da = eval(data)
                if self._file_print and self._file_fp:
                    print(da,file=self._file_fp)
                st = "{} {} {} {} {}".format(da['F'],da['R'],da['B'],da['L'],da['action'])
            except:
                pass
        
        print("[{3}] {0}  \t from {1} 消息：{2}".format(
                time.asctime(time.localtime()), client_addr, st,self._name))
        pass

class SafeCheckServer(UdpServer):
    def __init__(self):
        name='SafeCheck'
        ip = '127.0.0.1'
        port = 41998

        super().__init__(name,ip,port)
    
    def handle_recv_msg(self,msg,addr):
        if (msg.startswith("Initial:")):
            return
        elif (msg == 'shutdown'):
            Shutdown()
            return
        elif msg == 'quit':
            self._clients_lock.acquire()
            if addr == self._phy_client: 
                self._phy_client = None
            if addr == self._grd_client: 
                self._grd_client = None
            if addr == self._admin_client:
                self._admin_client = None
            self._clients_lock.release()
            return
        
        if (self._phy_client is None) or (self._grd_client is None):
            return
         
        space = msg.find(' ')
        data = msg[space+1:]

        if (data.startswith("?")):
            self.push_send_msg(msg,self._grd_client)
        elif (data.startswith("!")):
            self.push_send_msg(msg,self._phy_client)

class LocationServer(UdpServer):
    def __init__(self):
        name='Location'
        ip = '127.0.0.1'
        port = 41999

        super().__init__(name,ip,port)
    
    def handle_recv_msg(self,msg,addr):
        if (msg.startswith("Initial:")):
            self.push_send_msg(msg,self._grd_client)
            return
        elif (msg == 'shutdown'):
            Shutdown()
            return
        elif msg == 'quit':
            self._clients_lock.acquire()
            if addr == self._phy_client: 
                self._phy_client = None
            if addr == self._grd_client: 
                self._grd_client = None
            if addr == self._admin_client:
                self._admin_client = None
            self._clients_lock.release()
            return
        
        if (self._phy_client is None) or (self._grd_client is None):
            return
         
        space = msg.find(' ')
        data = msg[space+1:]

        if (data.startswith("?")):
            self.push_send_msg(msg,self._grd_client)
        elif (data.startswith("!")):
            self.push_send_msg(msg,self._phy_client)

def camera_main():
    camera_server = CameraTcpServer()
    camera_server.run()

def ir_sensor_main():
    ir_sensor_server = IrSensorUdpServer()
    ir_sensor_server.run()

def safe_check_main():
    safe_check_server = SafeCheckServer()
    safe_check_server.run()

def loc_main():
    loc_server = LocationServer()
    loc_server.run()

def main():
    thread_camera = Thread(target=camera_main) 
    thread_camera.start()

    thread_ir_sensor = Thread(target=ir_sensor_main) 
    thread_ir_sensor.start()

    thread_safe_check = Thread(target=safe_check_main) 
    thread_safe_check.start()

    thread_loc = Thread(target=loc_main) 
    thread_loc.start()

if __name__ == '__main__':
    main()
    # print(platform.PHYSICAL_MSG.is_online())
    # cv2.destroyAllWindows()